#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#define SYS_BIN_MARK_LEN 16
#define SYS_BIN_NUM_MAX 16

#define BIN_HEADER_LEN 0x1000UL
#define BIN_LEN_ALIGN 0x1000UL
#define OUTPUT_BIN_NAME "fwcode.bin"

#pragma pack(1)
typedef struct
{
	unsigned short first_page;
	unsigned short last_page;
	unsigned long code_length;
	unsigned long code_offset;
	unsigned short code_enable;
	unsigned short code_chk_sum;
} gdp_code_map_t;

typedef struct
{
	unsigned long code_offset;
	unsigned long code_length;
}code_entry_t;

typedef struct
{
	unsigned char adMark[SYS_BIN_MARK_LEN];			//ofst 0x0
	unsigned short rsvd0;							//ofst 0x10
	unsigned short code_num;
	unsigned char rsvd1[12];
	unsigned char rsvd2[16];						//ofst 0x20
	unsigned char rsvd3[64];						//ofst 0x30
	gdp_code_map_t bin_code_map[SYS_BIN_NUM_MAX];//ofst 0x70
	unsigned char rsvd4[256];						//ofst 0x170
	unsigned char rsvd5[32];						//ofst 0x270
	unsigned long bin_offset[SYS_BIN_NUM_MAX];		//ofst 0x290
	unsigned char rsvd6[336];						//ofst 0x2d0
	unsigned short code_num1;						//ofst 0x420
	unsigned char rsvd7[14];
	code_entry_t code_entry[SYS_BIN_NUM_MAX];		//ofst 0x430
} fw_gdp_bin_header_t;

#pragma pack()

#define LOG_DBG_EN

#if defined(LOG_DBG_EN)
	#define LOG_DBG(...) printf(__VA_ARGS__)
#else
	#define LOG_DBG(...)
#endif

#define LOG_ERR(...) printf(__VA_ARGS__)

int main(int argc, char *argv[])
{
	FILE *fp = NULL;
	int len = 0, bin_len = 0x200000; // 2M
	int bin_total_len = 0;
	unsigned char *bin_buf = NULL;
	unsigned char *wbuf = NULL;
	fw_gdp_bin_header_t *bin_header = NULL;
	unsigned long bin_ofst = 0;
	//LOG_DBG("[W]argc:%d\r\n", argc);
	if ((argc < 4) || ((argc - 1) % 3 != 0))
	{
		LOG_ERR("[E]argc not match!!!\r\n");
		goto error_exit;
	}

	bin_buf = (unsigned char *)malloc(bin_len);
	memset(bin_buf, 0, bin_len);
	bin_header = (fw_gdp_bin_header_t *)bin_buf;
	for (int i = 1; i < argc; i += 3)
	{
		if (0 == memcmp(argv[i], "NULL", 4) || 0 == memcmp(argv[i], "null", 4))
		{
			//LOG_DBG("[W][%d] is null!!\r\n", i);
			continue;
		}
		LOG_DBG("[%d]:%s %s %s\r\n", bin_header->code_num, argv[i], argv[i + 1], argv[i + 2]);
		if ((fp = fopen(argv[i], "rb")) == NULL)
		{
			LOG_ERR("[E]read bin %d:%s fail\r\n", i, argv[i]);
			goto error_exit;
		}

		fseek(fp, 0, SEEK_END);
		len = ftell(fp);
		bin_header->bin_code_map[bin_header->code_num].code_length = (len % BIN_LEN_ALIGN) ? ((len & ~(BIN_LEN_ALIGN - 1)) + BIN_LEN_ALIGN) : len;
		bin_header->bin_code_map[bin_header->code_num].code_offset = strtol(argv[i + 1], NULL, 16);
		bin_header->code_entry[bin_header->code_num].code_offset = bin_header->bin_code_map[bin_header->code_num].code_offset;
		bin_header->code_entry[bin_header->code_num].code_length = bin_header->bin_code_map[bin_header->code_num].code_length;
		bin_header->bin_code_map[bin_header->code_num].code_enable = 0xa1;
		bin_header->bin_code_map[bin_header->code_num].code_chk_sum = 0xffff;
		bin_header->bin_offset[bin_header->code_num] = (BIN_HEADER_LEN + bin_ofst);
		bin_ofst += bin_header->bin_code_map[bin_header->code_num].code_length;
		rewind(fp);
		fseek(fp, 0, SEEK_SET);
		fread((bin_buf + bin_header->bin_offset[bin_header->code_num]), 1, len, fp);
		fclose(fp);

		LOG_DBG("    len:%x %x bin_ofst:%x\r\n",
		        len,
		        bin_header->bin_code_map[bin_header->code_num].code_length,
		        bin_header->bin_offset[bin_header->code_num]);
		bin_header->code_num++;
	}
	bin_header->bin_code_map[bin_header->code_num-1].code_enable = 0xa3;
	bin_header->code_num1 = bin_header->code_num;
	memset(bin_header->rsvd1, 0xFF, sizeof(bin_header->rsvd1));
	memset(bin_header->rsvd3, 0xAE, sizeof(bin_header->rsvd3));
	memset(bin_header->rsvd4, 0xEE, sizeof(bin_header->rsvd4));
	bin_total_len = (BIN_HEADER_LEN + bin_ofst);
	LOG_DBG("bin_size:%x\r\n", bin_total_len);
	//LOG_DBG("code_num:%d\r\n", bin_header->code_num);

	wbuf = (unsigned char *)malloc(bin_total_len);
	memcpy(wbuf, bin_buf, bin_total_len);
	fp = fopen(OUTPUT_BIN_NAME, "wb");
	fwrite(wbuf, 1, bin_total_len, fp);
	fclose(fp);

	free(wbuf);
	free(bin_buf);
	return 0;
error_exit:
	return 0;
}
